package org.mozhu.mboot.core.i18n;

import com.google.common.collect.Maps;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.AbstractMessageSource;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class ReloadableMessageSource extends AbstractMessageSource implements ApplicationListener<MessageChangeEvent> {
	/** Milliseconds to hold the cache */
	private long cacheMillis = 24 * 3600 * 1000;
	/** reset everytime the timestamp was changed */
	private long refreshTimestamp = -1;
	private Map<String, Map<Locale, MessageHolder>> cacheMessages = new HashMap<String, Map<Locale, MessageHolder>>();
	private MessageResourceLoader messageResourceLoader;

	protected Map<String, Map<Locale, MessageHolder>> getCacheMessages() {
		synchronized (this.cacheMessages) {
			if (this.cacheMessages != null && !isNeedRefresh()) {
				return this.cacheMessages;
			}
			return refreshMessages();
		}
	}

	protected boolean isNeedRefresh() {
		return this.refreshTimestamp < 0 || (this.cacheMillis > 0 && System.currentTimeMillis() - this.refreshTimestamp > this.cacheMillis);
	}

	private Map<String, Map<Locale, MessageHolder>> refreshMessages() {
		Map<String, Map<Locale, MessageHolder>> messages = Maps.newHashMap();
		Map<String, Map<Locale, String>> loadMessages = messageResourceLoader.loadMessages();
		for (Map.Entry<String, Map<Locale, String>> entry : loadMessages.entrySet()) {
			String key = entry.getKey();
			Map<Locale, String> localeMap = entry.getValue();
			Map<Locale, MessageHolder> localeMessageMap = messages.get(key);
			if (localeMessageMap == null) {
				localeMessageMap = Maps.newHashMap();
				messages.put(key, localeMessageMap);
			}
			for (Map.Entry<Locale, String> localeMapEntry : localeMap.entrySet()) {
				Locale locale = localeMapEntry.getKey();
				String message = localeMapEntry.getValue();
				localeMessageMap.put(locale, new MessageHolder(locale, key, message));
			}
		}
		synchronized (this.cacheMessages) {
			this.cacheMessages.clear();
			this.cacheMessages.putAll(messages);
			refreshTimestamp = System.currentTimeMillis();
			return this.cacheMessages;
		}
	}

	@Override
	protected MessageFormat resolveCode(String code, Locale locale) {
		return getMessageFormat(code, locale);
	}

	protected MessageFormat getMessageFormat(String code, Locale locale) {
		Map<Locale, MessageHolder> localeMap = this.getCacheMessages().get(code);
		if (localeMap != null) {
			MessageHolder result = localeMap.get(locale);
			if (result != null) {
				return result.getMessageFormat();
			}
		}
		return null;
	}

	public void setCacheMillis(long cacheMillis) {
		this.cacheMillis = cacheMillis;
	}

	public void setMessageResourceLoader(MessageResourceLoader messageResourceLoader) {
		this.messageResourceLoader = messageResourceLoader;
	}

	@Override
	public void onApplicationEvent(MessageChangeEvent event) {
		String key = event.getKey();
		Locale locale = event.getLocale();
		String message = event.getMessage();
		synchronized (this.cacheMessages) {
			Map<Locale, MessageHolder> localeMap = this.cacheMessages.get(key);
			if (localeMap == null) {
				localeMap = Maps.newHashMap();
				this.cacheMessages.put(key, localeMap);
			}
			localeMap.put(locale, new MessageHolder(locale, key, message));
		}
	}

	public class MessageHolder {
		private String key;
		private String message;
		private Locale locale;
		private MessageFormat messageFormat;

		public MessageHolder(Locale locale, String key, String message) {
			this.locale = locale;
			this.key = key;
			this.message = message;
		}

		public String getKey() {
			return key;
		}

		public String getMessage() {
			return message;
		}

		public Locale getLocale() {
			return locale;
		}

		public MessageFormat getMessageFormat() {
			if (messageFormat == null) {
				// lazy init message format
				// no need to consider thread safe
				messageFormat = createMessageFormat(message, locale);
			}
			return messageFormat;
		}

	}

}
